#include "server.h"
#include <sstream>
#include <iostream>
#include "eventStore.h"
#include "listener.h"
#include "theEncryptor.h"

using namespace std;

server::server()
{
	link_id=0;
}

server::~server()
{

}

void
server::startServerThread(int serverPort_)
{
	serverPort = serverPort_;

	pthread_create(new pthread_t, NULL,&server::runServer,
			new accessHelper(this,(void*)0,sock,serverPort));
}


void
server::establishConnection(int sock_)
{

	com_params cp;

	sock=sock_;
	static int bufferSize=64;
	int clilen;
	struct sockaddr_in cli_addr;
	int n;
	clilen = sizeof(cli_addr);

	cp.sockChat = accept(sock, (struct sockaddr *) &cli_addr, (socklen_t*) &clilen);
	char namebuf[bufferSize];
	n = recv(cp.sockChat,namebuf,bufferSize,0); // obtain connected name
	if(n==0)return;
	//int length = strlen(namebuf);
	namebuf[n]='\0';
	std::cout<<"namebuf server: "<<namebuf<<" and n: "<<n<<std::endl;

	//std string str("Connection established to peer ");
	peerNick=std::string(namebuf);

	// now send my nick
	comms::sendNick(charNick,cp);

	// obtain ip address of connected peer here! Again, we can send this as an event
	char str[INET_ADDRSTRLEN];
	inet_ntop(AF_INET, &(cli_addr.sin_addr), str, INET_ADDRSTRLEN);

	stringstream ss("");
	ss << "Connection established to peer " << string(str) << " with username "<<string(namebuf)<<std::endl;
	eventStore es;
	es.message = ss.str();
	es.tag = "STATUS";
	es.type=7; // 7 so that peer is displayed
	es.extra=std::string(namebuf);
	pthread_mutex_lock(&listener::mutex);
	listener::eventQueue.push(es);
	pthread_mutex_unlock(&listener::mutex);

	// we are the server so need to do receiving!
	comms::encryptor->receivePublicKeyAndSetRSA(cp.sockChat,2048);

	ss.str("");
	ss << "Received public key OK"<<std::endl;

	es.message = ss.str();
	es.tag = "STATUS";
	es.type=3;
	pthread_mutex_lock(&listener::mutex);
	listener::eventQueue.push(es);
	pthread_mutex_unlock(&listener::mutex);


	ss.str("");
	ss << "Generating private key.."<<std::endl;
	es.message = ss.str();
	es.tag = "STATUS";
	es.type=3;
	pthread_mutex_lock(&listener::mutex);
	listener::eventQueue.push(es);
	pthread_mutex_unlock(&listener::mutex);

	// generate the symmetric cipher
	if(link_id==0)comms::encryptor->generateRandomBlowfishKey(64);

	ss.str("");
	ss << "Sending encrypted private key to client.."<<std::endl;
	es.message = ss.str();
	es.tag = "STATUS";
	es.type=3;
	pthread_mutex_lock(&listener::mutex);
	listener::eventQueue.push(es);
	pthread_mutex_unlock(&listener::mutex);

	// encrypt and send the symmetric cipher
	comms::encryptor->encryptAndSendSymmetricKey(cp.sockChat);

	ss.str("");
	ss << "Encrypted private key sent to client.."<<std::endl;
	es.message = ss.str();
	es.tag = "STATUS";
	es.type=3;
	pthread_mutex_lock(&listener::mutex);
	listener::eventQueue.push(es);
	pthread_mutex_unlock(&listener::mutex);

	// create a channel for file receiving protocol
	cp.sockFile_protocol = accept(sock,(struct sockaddr*)&cli_addr,(socklen_t*)&clilen);

	// event here indicating file sending protocol channel established

	// create two channels here: one for sending file and one for receiving
	cp.sockFile_send = accept(sock,(struct sockaddr*)&cli_addr,(socklen_t*)&clilen);
	cp.sockFile_receive = accept(sock,(struct sockaddr*)&cli_addr,(socklen_t*)&clilen);

	ss.str("");
	ss << "File transfer channels established.."<<std::endl;
	es.message = ss.str();
	es.tag = "STATUS";
	es.type=3;
	pthread_mutex_lock(&listener::mutex);
	listener::eventQueue.push(es);
	pthread_mutex_unlock(&listener::mutex);

	ss.str("");
	ss << "Connected!"<<std::endl;


	es.message = ss.str();
	es.tag = "STATUS";
	es.type=3;
	pthread_mutex_lock(&listener::mutex);
	listener::eventQueue.push(es);
	pthread_mutex_unlock(&listener::mutex);

	// now inform all connected peers that a new peer has joined the server
	// sending id of peer and the user name of the peer
	for(int p = 0;p<comms::all_com_params.size();p++)
	{
		std::stringstream ss("");
		ss<<"P"<<all_com_params.size()<<'\n'<<peerNick;
		std::string toSendString(ss.str());
		cm->sendMessage(toSendString,p);
	}

	// push back this new peer connection (store its connection parameters!)
	cp.peerNick=peerNick;
	comms::all_com_params.push_back(cp);

	// now inform newly connected peer of old other peers
	for(int p = 0;p<comms::all_com_params.size()-1;p++)
	{
		std::stringstream ss("");
		ss<<"P"<<p<<'\n'<<comms::all_com_params[p].peerNick;
		std::string toSendString(ss.str());
		cm->sendMessage(toSendString,comms::all_com_params.size()-1);
	}


	cm->startThreads(link_id);
	link_id++;

}

void
*server::runServer(void *tid)
{
	accessHelper* ah = static_cast<accessHelper*>(tid);
	server* This = ah->This;
	int sock = ah->sock;
	int serverPort = ah->serverPort;
	int clilen;
	struct sockaddr_in serv_addr;
	int n;

	eventStore es;

	sock = socket(AF_INET, SOCK_STREAM, 0);

	if (sock < 0)
	{

		es.message = "Error start server";
		es.tag = "STATUS";
		es.type=3;
		pthread_mutex_lock(&listener::mutex);
		listener::eventQueue.push(es);
		pthread_mutex_unlock(&listener::mutex);
	}
	else
	{
		bzero((char *) &serv_addr, sizeof(serv_addr));
		serv_addr.sin_family = AF_INET;
		serv_addr.sin_addr.s_addr = INADDR_ANY;
		serv_addr.sin_port = htons(serverPort);
		if (bind(sock, (struct sockaddr *) &serv_addr,
				 sizeof(serv_addr)) < 0)
		{
			es.message = "Error on binding to socket";
			es.tag = "STATUS";
			es.type=3;
			pthread_mutex_lock(&listener::mutex);
			listener::eventQueue.push(es);
			pthread_mutex_unlock(&listener::mutex);
		}
		else
		{
			listen(sock,5);

			std::stringstream ss("");
			ss << "Server started on port " << serverPort <<std::endl;
			es.message = ss.str();
			es.tag = "STATUS";
			es.type=3;
			pthread_mutex_lock(&listener::mutex);
			listener::eventQueue.push(es);
			pthread_mutex_unlock(&listener::mutex);


			// while loop here for incoming connections!
			while(1)
			{

				This->establishConnection(sock);

				/*
				client ph;
				ph.setListener(This->listener_);
				ph.establishIncomingClientCon(sock);
				This->peerHandlers.push_back(ph);
				*/

				usleep(2500);

			}

		}
	}

	pthread_exit(NULL);
}

